/***************************************************************************
 *   Copyright (C) 2008 by Thomas Richards   *
 *   thomi@colon   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "project.h"
#include "plugin.h"
#include "pluginmanager.h"

#include <QSettings>
#include <QDebug>
#include <QTemporaryFile>

Project::Project(const QDir &projectDir, QObject *parent)
 : QObject(parent),
m_projectDir(projectDir),
m_nCurrentState(STATE_INITIAL)
{
	connect(&m_tickTimer, SIGNAL(timeout()), this, SLOT(tick()));
}


Project::~Project()
{
}

void Project::createNewProject()
{
	// create blank config file:
	QSettings settings(m_projectDir.absoluteFilePath("projectSettings.ini"), QSettings::IniFormat);
	
	PluginManager &plugMan = PluginManager::instance();
	
	settings.setValue("getter", "copy");
	settings.setValue("browser", "simple");
	
	QStringList slScanners = plugMan.getAvailablePlugins(PluginManager::PLUGIN_TYPE_SCANNER);

	settings.setValue("scanners", slScanners);
	QStringList slOutput = plugMan.getAvailablePlugins(PluginManager::PLUGIN_TYPE_OUTPUT);
	if (slOutput.count() > 0)
		settings.setValue("output", slOutput.at(0));
	else
	{
		settings.setValue("output","");
		qWarning("No output plugins registerred!");
	}
	
	// write plugin configs, even though many of them won't be used:
	QList<PluginManager::tPluginType> pluginTypes;
	pluginTypes << PluginManager::PLUGIN_TYPE_BROWSER 
			<< PluginManager::PLUGIN_TYPE_GETTER 
			<< PluginManager::PLUGIN_TYPE_SCANNER
			<< PluginManager::PLUGIN_TYPE_OUTPUT;
	
	foreach(PluginManager::tPluginType type, pluginTypes)
	{
		QStringList plugins = plugMan.getAvailablePlugins(type);
		foreach(QString strPlugin, plugins)
		{
			Plugin *plug = plugMan.getPluginByName(strPlugin, type);
			if (plug)
			{
				settings.beginGroup(strPlugin);
				plug->writeDefaultConfig(&settings);
				settings.endGroup();
			}
		}
	}
}

void Project::runProject(const QString & strProjectFile)
{
	m_projectFile = strProjectFile;
	Q_ASSERT(m_projectFile.exists());
	
	
	// tick once a second by default:
	m_tickTimer.setSingleShot(false);
	m_tickTimer.start(1000);
}

void Project::tick()
{
	quint16 nNextTickTime = 1000;
	
	// will need the plugin manager:
	PluginManager &plugMan = PluginManager::instance();
	QDir tmp = m_projectFile.absoluteDir();
	
	// project state machine:
	switch (m_nCurrentState)
	{
		// initial state - project file is guaranteed to exist at this point, or we wouldn't have gotten here.
		// read project settings, get a list of plugins, make sure they all exist and fire off the getter plugin:
		case STATE_INITIAL:
		{
			// open the file for reading:
			QSettings settings(m_projectFile.absoluteFilePath(), QSettings::IniFormat);
	
			// work out what getter and scanner the user wants to use:
			m_strGetter = settings.value("getter").toString();
			m_strBrowser = settings.value("browser").toString();
			m_strOutput = settings.value("output").toString();
	
	
			if (m_strGetter.isNull() || m_strBrowser.isNull())
			{
				qWarning() << "Error - must specify a getter and a scanner plugin";
				return;
			}
	
			m_slScanners = settings.value("scanners").toStringList();
			if (m_slScanners.empty())
			{
				qWarning() << "Error - at least one scanner plugin must be specified";
				return;
			}
	
			
	
			m_pCopyPlugin = plugMan.getPluginByName(m_strGetter, PluginManager::PLUGIN_TYPE_GETTER);
			if (!m_pCopyPlugin)
			{
				qWarning() << "Error - getter plugin" << m_strGetter << "not found";
				return;
			}
			
			// make getter plugin arguments:
			QStringList slArgs;
			slArgs << "--cf" << m_projectFile.absoluteFilePath() << "--dstDir" << QDir::toNativeSeparators(tmp.absoluteFilePath("tmp"));
	 
			m_pCopyPlugin->run(slArgs);
			// MOVE:
			setState(STATE_WAIT_COPY);
		}
		break;
		// wait for copy plugin to end. WHen it does, try and get the browser plugin and start that up.
		case STATE_WAIT_COPY:
		{
			// MOVE
			if (m_pCopyPlugin->state() == QProcess::NotRunning)
			{
				qDebug() << "getter stdout:" << m_pCopyPlugin->readAllStandardOutput();
				qDebug() << "getter stderr:" << m_pCopyPlugin->readAllStandardError();
		 
				// try and get the browser plugin next:
				m_pBrowserPlugin = plugMan.getPluginByName(m_strBrowser, PluginManager::PLUGIN_TYPE_BROWSER);
				if (!m_pBrowserPlugin)
				{
					qWarning() << "Error - unable to find browser plugin:" << m_strBrowser;
					return;
				}
				// make browser plugin arguments:
				QStringList slArgs;
				slArgs << "--cf" << m_projectFile.absoluteFilePath() << "--rootDir" << QDir::toNativeSeparators(tmp.absoluteFilePath("tmp"));
	 
				m_pBrowserPlugin->run(slArgs);
				setState(STATE_WAIT_BROWSE);
			}
		}
		break;
		case STATE_WAIT_BROWSE:
		{
			if (m_pBrowserPlugin->state() == QProcess::NotRunning)
			{
				m_slFilesToScan = m_pBrowserPlugin->readAllStandardOutput().split('\n');
				
				// write results to file:
				m_outputFile.setFileTemplate(QDir::temp().filePath("cppAnalyse"));
				m_outputFile.open();
				m_outputFile.setAutoRemove(false);
				setState(STATE_SCANNING);
			}
		}
		break;
		
		case STATE_SCANNING:
		{
			nNextTickTime = 10;
			if (m_slFilesToScan.isEmpty())
			{
				// MOVE:
				m_outputFile.flush();
				setState(STATE_OUTPUT);
			}
			else
			{
				QByteArray strFileToScan = m_slFilesToScan.takeFirst();
				if (strFileToScan.simplified() == "")
					break;
				
				qDebug() << "Scanning file:" << strFileToScan;
				foreach (QString strScanner, m_slScanners)
				{
					Plugin *plug = plugMan.getPluginByName(strScanner, PluginManager::PLUGIN_TYPE_SCANNER);
					if (!plug)
					{
						qWarning() << "Unable to load scanner:" << strScanner;
						continue;
					}
					// create arguments:
					QStringList slArgs;
					QFileInfo fi(strFileToScan);
					slArgs << "--cf" << m_projectFile.absoluteFilePath() << "--inputFile" << QDir::toNativeSeparators(fi.absoluteFilePath());
					plug->run(slArgs);
					
					// TODO - need to run plugins in a queue so we run them much much faster, and in parallel.
					if (plug->state() != QProcess::NotRunning)
						plug->waitForFinished(-1);
					
					QByteArray baStdError = plug->readAllStandardError();
					if (baStdError.trimmed() != "")
					{
						qWarning() <<  baStdError;
					}
					QString strStdOut = plug->readAllStandardOutput();
					if (strStdOut != "")
					{
						QStringList slResults = strStdOut.split('\n');
						while (!slResults.isEmpty())
						{
							QString strResult = slResults.takeFirst().simplified();
							if (strResult != "")
								m_outputFile.write(QString("%1|%2\n").arg(strScanner).arg(strResult).toAscii());
						}
					}
				}
			}
		}
		break;
		// run the output plugin with contents of temp file:
		case STATE_OUTPUT:
		{
			if (m_strOutput.trimmed() == "")
			{
				qDebug() << "No output plugin defined. raw output in:" << m_outputFile.fileName();
				// MOVE / ERROR
				setState(STATE_DONE);
				break;
			}
	
			// get the output plugin:
			Plugin *plug = plugMan.getPluginByName(m_strOutput, PluginManager::PLUGIN_TYPE_OUTPUT);
			if (!plug)
			{
				qWarning() << "Could not find output plugin" << m_strOutput << "raw results available here:" << m_outputFile.fileName();
				setState(STATE_DONE);
				break;
			}
	
			QStringList slArgs;
			slArgs << "--cf" << m_projectFile.absoluteFilePath() << "--inputFile" << m_outputFile.fileName();
			plug->run(slArgs);
			plug->waitForFinished(-1);
	
			qDebug() << "output stdout:" << plug->readAllStandardOutput();
			qDebug() << "output stderr:" << plug->readAllStandardError();
			m_outputFile.remove();
			setState(STATE_DONE);
		}
		break;
		case STATE_DONE:
		{
			qDebug() << "DONE";
			return;	//< don't fire singleshot timer again.
		}
		break;
		
	}
	// start the next tick:
	m_tickTimer.start(nNextTickTime);
}

void Project::setState(tProjectState newState)
{
	m_nCurrentState = newState;
	qDebug() << "Changing state to:" << newState;
	emit stateChanged(m_nCurrentState);
}


